/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.action;

import com.daikit.daikit4gxt.client.DkMain;
import com.daikit.daikit4gxt.client.controller.BaseErrorHandler;
import com.daikit.daikit4gxt.client.log.BaseLogger;
import com.google.gwt.user.client.rpc.AsyncCallback;


/**
 * Given to a RPC call. This RPC call should be made in an Action.run() method.
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <T>
 *           The type of the return of the underlying RPC.
 * @see BaseAction
 */
public abstract class BaseAsyncCallback<T> implements AsyncCallback<T>
{

	private final BaseLogger log = BaseLogger.getLog(BaseAsyncCallback.class);

	private final BaseAction<T> parentAction;
	private final boolean propagateFailure;

	/**
	 * Constructor with parent action (the action which will call this callback)
	 * 
	 * @param parentAction
	 *           the parent action
	 * @param propagateFailure
	 *           a boolean indicating whether failure should be propagated to {@link BaseErrorHandler} to be treated in
	 *           the standard error handling mechanism or not (if false, then the chain will swallow eventual error that
	 *           should be treated in {@link #processFailure(Throwable)} method)
	 */
	public BaseAsyncCallback(final BaseAction<T> parentAction, final boolean propagateFailure)
	{
		if (parentAction == null)
		{
			throw new RuntimeException("PARENT ACTION SHOULD NOT BE NULL . DON'T CREATE BaseAsyncCallback OUT OF A BaseAction");
		}
		this.parentAction = parentAction;
		this.propagateFailure = propagateFailure;
	}

	@Override
	public final void onFailure(final Throwable exception)
	{
		if (processFailure(exception))
		{
			DkMain.errorHandler().handle(parentAction, exception);
		}
		parentAction.onFailure(exception);
	}

	@Override
	public final void onSuccess(final T result)
	{
		try
		{
			// log.info("BaseAsyncCallback onSuccess(" + result + ") parentAction=[" + parentAction + "]") ;
			if (!parentAction.isChainPaused() && !parentAction.isChainStopped() && !parentAction.isChainTimeout())
			{
				processSuccess(result);
			}
			else
			{
				log.info("BaseAsyncCallback  << processSuccess(...)  NOT EXECUTED >> onSuccess(" + result + "), parentAction=["
						+ parentAction + "]");
				log.info("--> parentAction.isChainPaused()=" + parentAction.isChainPaused() + ", parentAction.isChainStopped()="
						+ parentAction.isChainStopped() + ", parentAction.isChainTimeout()=" + parentAction.isChainTimeout());
			}
			parentAction.onSuccess(result);
		}
		catch (final Throwable t)
		{
			// TODO see propagate failure here.
			parentAction.onClientActionRunningException(t);
		}
	}

	/**
	 * Method to be implemented to process the result given to the callback. This function is called within
	 * {@link #onSuccess(Object)} method (which should not be overridden).
	 * 
	 * @param result
	 *           the result. It can come from a RPC call or whatever given to {@link #onSuccess(Object)} method.
	 */
	public abstract void processSuccess(final T result);

	/**
	 * Return whether or not the exception should be processed by the automatic process. Generally if this function is
	 * overridden to process the exception, the method will return false to avoid a second processing by the default
	 * process.
	 * 
	 * @param caught
	 * @return true if the default process should be applied to manage with this exception.
	 */
	public boolean processFailure(final Throwable caught)
	{
		return true;
	}

	protected void stopChain()
	{
		parentAction.stopChain();
	}

	protected void pauseChain()
	{
		parentAction.pauseChain();
	}

	protected void resumeChain(final T result)
	{
		parentAction.resumeChain(result);
	}

	/**
	 * @return the action passed to this callback at creation time.
	 */
	public BaseAction<T> getParentAction()
	{
		return parentAction;
	}

	/**
	 * @return whether the failure should be propagated
	 */
	public boolean isPropagateFailure()
	{
		return propagateFailure;
	}
}
